import { PlusOutlined, DeleteOutlined } from '@ant-design/icons'
import { truthy } from '@gnlab/utils'
import { App, Tooltip, Spin } from 'antd'
import formatAssetUrl from '@/desktop/logics/format-asset-url'
import { getImageInfo, scaleImage } from '@/lib/utils'
import { commonS } from './common-styles'
import { type BaseProps, type CommonProps, propsWithDefaults, useUpload } from './logics'

function UploadImage(props: SingleProps | (MultipleProps & { multiple?: true })) {
  if ('multiple' in props && props.multiple) {
    return <UploadMultipleImage {...(props as MultipleProps)} />
  } else {
    return <UploadSingleImage {...(props as SingleProps)} />
  }
}
export { UploadImage }

type SingleProps = BaseProps<string> &
  Pick<CommonProps, 'rectLimit' | 'mimeType'> & {
    scale?: boolean // 若为 true，且图片超出 rectLimit 时，会缩放图片
  }

export function UploadSingleImage(props: SingleProps) {
  const { message } = App.useApp()
  const {
    cdn,
    tips,
    sizeLimit,
    rectLimit,
    scale = false,
    mimeType,
    value,
    onChange,
  } = propsWithDefaults(props)
  const [uploading, upload] = useUpload(cdn)
  const inputRef = useRef<HTMLInputElement>(null)

  const inputOnChange = useCallback(
    async (e: React.ChangeEvent<HTMLInputElement>) => {
      let file: File | Blob | undefined = e.target.files![0]
      e.target.value = ''
      if (file) {
        if (sizeLimit) {
          const size = file.size
          if (size > 1024 * 1024 * sizeLimit)
            return void message.warning(`图片文件不能大于${sizeLimit}M`)
        }

        if (rectLimit) {
          if (scale) {
            const scaleRes = await scaleImage(file, rectLimit.width, rectLimit.height)
            if (scaleRes.success) file = scaleRes.data
            else return void message.warning(`图片缩放失败：${scaleRes.error}`)
          } else {
            const rect = await getImageInfo(file) // eslint-disable-line no-await-in-loop
            if (rect.width > rectLimit.width || rect.height > rectLimit.height)
              return void message.warning(
                `图片尺寸不能大于${rectLimit.width} * ${rectLimit.height}像素`,
              )
          }
        }

        const uploaded = await upload(file)
        if (uploaded) onChange(uploaded.url)
      }
    },
    [sizeLimit, rectLimit, scale, upload, onChange, message],
  )

  return <div css={[commonS.upload, s.uploadImage]}>
    {truthy(value) ? (
      <Tooltip title="点击重新上传" placement="right">
        <div
          className="image"
          onClick={() => inputRef.current?.click()}
          style={{ backgroundImage: `url("${formatAssetUrl(value)}")` }}
        ></div>
      </Tooltip>
    ) : (
      <div css={s.uploadTrigger}>
        <Spin spinning={uploading}>
          <label onClick={() => inputRef.current?.click()}>
            <PlusOutlined />
          </label>
        </Spin>
      </div>
    )}
    <input
      type="file"
      accept={mimeType || '.jpg,.jpeg,.png'}
      ref={inputRef}
      onChange={inputOnChange}
    />
    {truthy(tips) ? <div css={commonS.uploadTips}>{tips}</div> : null}
  </div>
}

type MultipleProps = BaseProps<string[]> &
  Pick<CommonProps, 'rectLimit' | 'mimeType'> & {
    limit?: number // 支持多选的情况下，最多选几张
    scale?: boolean // 若为 true，且图片超出 rectLimit 时，会缩放图片
  }

export function UploadMultipleImage(props: MultipleProps) {
  const { message } = App.useApp()
  const {
    limit = 0,
    tips,
    sizeLimit,
    rectLimit,
    scale = false,
    cdn,
    mimeType,
    onChange,
  } = propsWithDefaults(props)
  const value = useMemo(() => props.value ?? [], [props.value])
  const [uploading, upload] = useUpload(cdn)

  const existNumber = value.length

  const remove = useCallback(
    (index: number) => {
      if (value.length <= index) return
      const images = [...value]
      images.splice(index, 1)
      onChange(images)
    },
    [value, onChange],
  )

  const inputOnChange = useCallback(
    async (e: React.ChangeEvent<HTMLInputElement>) => {
      let files = [...(e.target.files ?? [])] as (File | Blob)[]
      e.target.value = ''
      if (files.length) {
        if (limit && existNumber + files.length > limit) {
          files = files.slice(0, limit - existNumber)
        }

        if (sizeLimit) {
          for (const file of files) {
            const size = file.size
            if (size > 1024 * 1024 * sizeLimit) {
              void message.warning(`图片文件不能大于${sizeLimit}M`)
              return
            }
          }
        }

        if (rectLimit) {
          if (scale) {
            const scaledFiles: (Blob | File)[] = []
            for (const file of files) {
              const res = await scaleImage(file, rectLimit.width, rectLimit.height) // eslint-disable-line no-await-in-loop
              if (res.success) scaledFiles.push(res.data)
              else void message.warning(`图片缩放失败：${res.error}`)
            }
            files = scaledFiles
          } else {
            for (const file of files) {
              const rect = await getImageInfo(file) // eslint-disable-line no-await-in-loop
              if (rect.width > rectLimit.width || rect.height > rectLimit.height) {
                void message.warning(`图片尺寸不能大于${rectLimit.width} * ${rectLimit.height}像素`)
                return
              }
            }
          }
        }

        const uploaded = await upload(files)
        if (uploaded.length) onChange([...value, ...uploaded.map(v => v.url)])
      }
    },
    [existNumber, limit, sizeLimit, rectLimit, scale, value, onChange, upload, message],
  )

  return <div css={[commonS.upload, s.uploadMultipleImage]}>
    <div className="qun-upload-multiple-image-images">
      {value.map((url, index) => <div
        key={`${index}`}
        onClick={() => remove(index)}
        className="qun-upload-multiple-image-image"
        style={{ backgroundImage: `url("${formatAssetUrl(url)}")` }}
      >
        <DeleteOutlined />
      </div>)}

      {!limit || existNumber < limit ? (
        <div css={s.uploadTrigger} className="qun-upload-multiple-image-trigger">
          <Spin spinning={uploading}>
            <label>
              <PlusOutlined />
              <input
                type="file"
                accept={mimeType || '.jpg,.jpeg,.png'}
                multiple
                onChange={inputOnChange}
              />
            </label>
          </Spin>
        </div>
      ) : null}
    </div>

    {truthy(tips) ? <div css={[commonS.uploadTips, s.multipleUploadTips]}>{tips}</div> : null}
    {!truthy(tips) && limit ? (
      <div css={[commonS.uploadTips, s.multipleUploadTips]}>支持最多上传 {limit} 张图片</div>
    ) : null}
  </div>
}

const s = {
  uploadTrigger: css`
    width: 55px;
    label {
      align-items: center;
      border-radius: 2px;
      border: 1px solid #555;
      color: #555;
      cursor: pointer;
      display: flex;
      font-size: 35px;
      height: 55px;
      justify-content: center;
      transition: all 0.3s linear;
      user-select: none;
      &:hover {
        border-color: #6eb1ff;
        color: #6eb1ff;
      }
      &:active {
        border-color: #2e6fd1;
        color: #2e6fd1;
      }
    }
  `,
  uploadImage: css`
    .image {
      background-position: center center;
      background-repeat: no-repeat;
      background-size: contain;
      border-radius: 2px;
      cursor: pointer;
      height: 55px;
      width: 55px;
    }
  `,
  uploadMultipleImage: css`
    .qun-upload-multiple-image-images {
      display: flex;
      flex-wrap: wrap;

      & > * {
        margin-bottom: 12px;
        margin-right: 12px;
      }
    }
    .qun-upload-multiple-image-image {
      align-items: center;
      background-position: center center;
      background-repeat: no-repeat;
      background-size: contain;
      border-radius: 2px;
      cursor: pointer;
      display: flex;
      height: 55px;
      justify-content: center;
      position: relative;
      width: 55px;

      &:after {
        background-color: rgba(#000, 0);
        bottom: 0;
        content: '';
        left: 0;
        position: absolute;
        right: 0;
        top: 0;
        transition: 0.2s background-color;
      }
      &:hover:after {
        background-color: rgba(#000, 0.5);
      }
      :global(.anticon) {
        color: #fff;
        font-size: 24px;
        opacity: 0;
        position: relative;
        transition: 0.2s opacity;
        z-index: 1;
      }
      &:hover :global(.anticon) {
        opacity: 1;
      }
    }
  `,
  multipleUploadTips: css`
    margin: 0;
  `,
}
